/*-----------------------------------------------------------------------------
 * @file   hal_can.c
 * @brief  Controller Area Network (CAN) driver using bxCAN
 * @author WLH<wanglehui8357@163.com>
 ----------------------------------------------------------------------------*/
#include "hal_can.h"
#include "hal_irq.h"
#include "hal_gpio.h"
#include "board.h"
/*-----------------------------------------------------------------------------
 * TRACE Tools
 *---------------------------------------------------------------------------*/
#define USE_CAN_TRACE 0U

#if (USE_CAN_TRACE == 0U)
#define CAN_TRACE(...)
#define CAN_TRACE_I(...)
#define CAN_TRACE_W(...)
#define CAN_TRACE_E(...)
#else
#define CAN_TRACE(...)   trace_out(__VA_ARGS__)
#define CAN_TRACE_I(...) trace_info(__VA_ARGS__)
#define CAN_TRACE_W(...) trace_warn(__VA_ARGS__)
#define CAN_TRACE_E(...) trace_error(__VA_ARGS__)
#endif

/*-----------------------------------------------------------------------------
 * Macros define
-----------------------------------------------------------------------------*/
#define CAN_BUS_CNT     1u
#define CAN_TIME_OUT_MS 5u
#define CAN_STD_MAX_DL  8u                  // Max data length for CAN2.0
#define MAX_STD_MSG_ID  (0xFFFFu >> 5u)     // 11 bits for standard ID
#define MAX_EXT_MSG_ID  (0xFFFFFFFFu >> 3u) // 29 bits for extended ID

/*-----------------------------------------------------------------------------
 * local variable
-----------------------------------------------------------------------------*/
typedef struct
{
    CAN_RX_RDY_CB rx_cb;  /* Call back function for rx ready    */
    CAN_TX_CPLT_CB tx_cb; /* Call back function for tx complete */
    CAN_ERROR_CB err_cb;  /* Call back function for error occur */

    can_receive_message_struct rx_msg;
    can_trasnmit_message_struct tx_msg;
} gd32_can_bus_t;

static gd32_can_bus_t _canbus[CAN_BUS_CNT];

/*-----------------------------------------------------------------------------
 * local funtions
-----------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
 * public functions
-----------------------------------------------------------------------------*/
bool hal_can_init(const U8 bus_id)
{
    UNUSED(bus_id);

    /* enable can clock */
    rcu_periph_clock_enable(RCU_CAN);
    rcu_periph_clock_enable(RCU_GPIOA);

    /* configure CAN GPIO */
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4);
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4);
    gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_4);

    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5);
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
    gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_5);

    /* CAN 收发器工作模式控制 */
    hal_gpio_set(HAL_GPIO_PIN_2);

    can_parameter_struct can_param;
    can_filter_parameter_struct can_filter;
    /* initialize CAN structures */
    can_struct_para_init(CAN_INIT_STRUCT, &can_param);
    can_struct_para_init(CAN_FILTER_STRUCT, &can_filter);
    /* initialize CAN register */
    can_deinit();

    /* initialize CAN */
    can_param.time_triggered = DISABLE;
    can_param.auto_bus_off_recovery = ENABLE;
    can_param.auto_wake_up = DISABLE;
    can_param.auto_retrans = ENABLE;
    can_param.rec_fifo_overwrite = DISABLE;
    can_param.trans_fifo_order = DISABLE;
    can_param.working_mode = CAN_NORMAL_MODE;
    can_param.resync_jump_width = CAN_BT_SJW_1TQ;
    can_param.time_segment_1 = CAN_BT_BS1_12TQ;
    can_param.time_segment_2 = CAN_BT_BS2_3TQ;

    /* baudrate 1Mbps */
    can_param.prescaler = 2;
    if (SUCCESS != can_init(&can_param))
    {
        trace_error("[CAN0]:Init fail");
    }

    /* initialize filter */
    can_filter.filter_number = 0;
    can_filter.filter_mode = CAN_FILTERMODE_MASK;
    can_filter.filter_bits = CAN_FILTERBITS_32BIT;
    can_filter.filter_list_high = 0x0000;
    can_filter.filter_list_low = 0x0000;
    can_filter.filter_mask_high = 0x0000;
    can_filter.filter_mask_low = 0x0000;
    can_filter.filter_fifo_number = CAN_FIFO0;
    can_filter.filter_enable = ENABLE;
    can_filter_init(&can_filter);

    /* enable CAN receive FIFO0 not empty interrupt */
    can_interrupt_enable(CAN_INTEN_RFNEIE0);

    /* configure CAN0 NVIC */
    nvic_irq_enable(USBD_LP_CAN_RX0_IRQn, 0);

    return true;
}

bool hal_can_fd_supported(const U8 bus_id)
{
    UNUSED(bus_id);
    return false;
}

void hal_can_reg_rx_ready(const U8 bus_id, CAN_RX_RDY_CB cb_fun)
{
    assert(bus_id < CAN_BUS_CNT);
    if (bus_id >= CAN_BUS_CNT)
    {
        return;
    }
    gd32_can_bus_t* dev = &_canbus[bus_id];

    CRITICAL_SECTION_ENTER
    dev->rx_cb = cb_fun;
    CRITICAL_SECTION_LEAVE
}

void hal_can_reg_tx_cplt(const U8 bus_id, CAN_TX_CPLT_CB cb_fun)
{
    assert(bus_id < CAN_BUS_CNT);
    if (bus_id >= CAN_BUS_CNT)
    {
        return;
    }
    gd32_can_bus_t* dev = &_canbus[bus_id];

    CRITICAL_SECTION_ENTER
    dev->tx_cb = cb_fun;
    CRITICAL_SECTION_LEAVE
}

void hal_can_reg_err_occur(const U8 bus_id, CAN_ERROR_CB cb_fun)
{
    UNUSED(bus_id);
    UNUSED(cb_fun);
}

bool hal_can_transmit(const U8 bus_id, U32 msg_id, U8* data, U8 size)
{
    assert(bus_id < CAN_BUS_CNT);
    assert(NULL != data);

    if (bus_id >= CAN_BUS_CNT)
    {
        return false;
    }

    if (msg_id > MAX_EXT_MSG_ID)
    {
        CAN_TRACE_E("[CAN]:Invalid message ID %d", msg_id);
        return false;
    }

    assert(size <= CAN_STD_MAX_DL);
    if (size > CAN_STD_MAX_DL)
    {
        size = CAN_STD_MAX_DL;
    }

    gd32_can_bus_t* dev = &_canbus[bus_id];

    dev->tx_msg.tx_sfid = msg_id;
    dev->tx_msg.tx_efid = msg_id;
    dev->tx_msg.tx_ff = (msg_id > MAX_STD_MSG_ID) ? CAN_FF_EXTENDED : CAN_FF_STANDARD;
    dev->tx_msg.tx_ft = CAN_FT_DATA;
    dev->tx_msg.tx_dlen = size;
    if (NULL != data)
    {
        /* Copy transmit payload data */
        memcpy(&dev->tx_msg.tx_data, data, size);
    }
    else
    {
        /* Reset transmit payload to zero */
        memset(&dev->tx_msg.tx_data, 0, size);
    }

    /* get device control */
    const uint8_t mb_index = can_message_transmit(&dev->tx_msg);

    /* waiting for transmit completed */
    // while(CAN_TRANSMIT_OK != can_transmit_states(mb_index)) {}

    /* relese device control */
    return CAN_NOMAILBOX != mb_index;
}

bool hal_can_set_bitrate(const U8 bus_id, const U8 nbr, const U8 dbr)
{
    UNUSED(bus_id);
    UNUSED(nbr);
    UNUSED(dbr);

    // Not supported yet
    return false;
}

/*------------------------------------------------------------------------------
 * IRQHandlers for interrupt.(CAN0 RX0 exception)
------------------------------------------------------------------------------*/
void USBD_LP_CAN_RX0_IRQHandler(void)
{
    /* check the receive message */
    can_message_receive(CAN_FIFO0, &_canbus[0].rx_msg);

    if(_canbus[0].rx_cb)
    {
        can_frame_t frame;
        frame.id = _canbus[0].rx_msg.rx_ff == CAN_FF_STANDARD? _canbus[0].rx_msg.rx_sfid : _canbus[0].rx_msg.rx_efid;
        frame.len = _canbus[0].rx_msg.rx_dlen;
        frame.data = _canbus[0].rx_msg.rx_data;
        _canbus[0].rx_cb(0, &frame);
    }
}
